//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using Microsoft.TridentWordAddIn.Common;
using System.Collections.ObjectModel;

namespace Microsoft.TridentWordAddIn.Presentation
{
    public partial class DashboardTaskPane : BaseUserControl, ITaskPaneControl, IDisposable
    {
        Dictionary<Guid, ContentItem> idToContentItem;

        /// <summary>
        /// Initializes a new instance of the <see cref="DashboardTaskPane"/> class.
        /// </summary>
        public DashboardTaskPane()
        {
            base.Initialize(false);
            this.InitializeComponent();
            this.ControllerInstance.OnAssociationChanged += new EventHandler<AssociationChangedEventArgs>(Controller_OnAssociationChanged);
            this.RefreshAssociationsList(ControllerInstance.GetAllWorkflowAssociations(this.DocumentKey));
        }

        #region Private Methods

        private delegate void UIControllerDelegate(ICollection<WorkflowAssociation> workflowAssociations);

        /// <summary>
        /// Handles the OnAssociationChanged event of the Controller control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Microsoft.TridentWordAddIn.Common.AssociationChangedEventArgs"/> instance containing the event data.</param>
        private void Controller_OnAssociationChanged(object sender, AssociationChangedEventArgs e)
        {
            if (e.DocumentKey == this.DocumentKey)
            {
                if (this.InvokeRequired)
                {
                    this.BeginInvoke(new UIControllerDelegate(this.RefreshAssociationsList), e.Associations);
                }
                else
                {
                    this.RefreshAssociationsList(e.Associations);
                }
            }
        }

        /// <summary>
        /// Updates the pipeline associations list.
        /// </summary>
        /// <param name="associationList">The association list.</param>
        private void RefreshAssociationsList(ICollection<WorkflowAssociation> associationList)
        {
            Trace.WriteLine("Dashboard - Updating Workflow Associations List");
            this.lvWorkflowAssociations.Items.Clear();
            this.imlThumbNail.Images.Clear();
            this.btnScrollToImage.Enabled = false;

            foreach (WorkflowAssociation association in associationList)
            {
                this.AddAssociation(association);
            }

            this.ctrDocumentContents.ContentItems = this.GetContentItem(associationList);
            Trace.WriteLine("Dashboard - Workflow Associations List Updated Successfully");
        }

        /// <summary>
        /// Gets the content item.
        /// </summary>
        /// <param name="associationList">The association list.</param>
        /// <returns>ContentItem[]</returns>
        private ContentItem[] GetContentItem(ICollection<WorkflowAssociation> associationList)
        {
            Model model = ControllerInstance.GetModelByDocumentKey(DocumentKey);
            List<ContentItem> results = new List<ContentItem>();
            this.idToContentItem = new Dictionary<Guid, ContentItem>();
            bool created;

            foreach (WorkflowAssociation association in associationList)
            {
                Workflow workflow = model.Workflows[association.WorkflowId];
                if (workflow != null && !workflow.Linked)
                {
                    created = false;
                    ContentItem workflowItem = this.GetWorkflowContentItem(association.Name, workflow, ref created);
                    workflowItem.Size = model.Modules[workflow.Id].File.Length;
                    if (created)
                    {
                        results.Add(workflowItem);
                    }
                }

                foreach (ShallowJobOutput output in association.Job.Outputs)
                {
                    created = false;
                    ContentItem ouputItem = this.GetOuputContentItem(association.Name, output, ref created);
                    ouputItem.Size = model.Modules[output.Id].File.Length;
                    if (created)
                    {
                        results.Add(ouputItem);
                    }
                }
            }

            return results.ToArray();
        }

        /// <summary>
        /// Gets the workflow content item.
        /// </summary>
        /// <param name="associationName">Name of the association.</param>
        /// <param name="workflow">The workflow.</param>
        /// <param name="created">if set to <c>true</c> [created].</param>
        /// <returns>ContentItem</returns>
        private ContentItem GetWorkflowContentItem(string associationName, Workflow workflow, ref bool created)
        {
            ContentItem item = null;
            this.idToContentItem.TryGetValue(workflow.Id, out item);
            if (item == null)
            {
                item = new ContentItem(workflow.Name, 0, ContentItemType.Workflow);
                this.idToContentItem.Add(workflow.Id, item);
                created = true;
            }

            if (!item.BelongsTo.Contains(associationName))
            {
                item.BelongsTo.Add(associationName);
            }

            return item;
        }

        /// <summary>
        /// Gets the ouput content item.
        /// </summary>
        /// <param name="associationName">Name of the association.</param>
        /// <param name="output">The output.</param>
        /// <param name="created">if set to <c>true</c> [created].</param>
        /// <returns></returns>
        private ContentItem GetOuputContentItem(string associationName, ShallowJobOutput output, ref bool created)
        {
            ContentItem moduleItem = null;
            this.idToContentItem.TryGetValue(output.Id, out moduleItem);
            if (moduleItem == null)
            {
                created = true;
                moduleItem = new ContentItem(output.Name, 0, ContentItemType.ResultFile);
                this.idToContentItem.Add(output.Id, moduleItem);
            }

            if (!moduleItem.BelongsTo.Contains(associationName))
            {
                moduleItem.BelongsTo.Add(associationName);
            }

            return moduleItem;
        }

        /// <summary>
        /// Adds the association.
        /// </summary>
        /// <param name="association">The association.</param>
        private void AddAssociation(WorkflowAssociation association)
        {
            Trace.WriteLine("Dashboard - Adding Association");
            foreach (ListViewItem item in this.lvWorkflowAssociations.Items)
            {
                WorkflowAssociation tempWorkflowAssociation = item.Tag as WorkflowAssociation;
                if (tempWorkflowAssociation.Picture.Equals(association.Picture))
                {
                    item.Text += ", " + association.Name;
                    item.ToolTipText = item.Text;
                    return;
                }
            }

            if (association.ThumbnailImage == null)
            {
                return;
            }

            this.imlThumbNail.Images.Add(this.imlThumbNail.Images.Count.ToString(), association.ThumbnailImage);
            ListViewItem newItem = this.lvWorkflowAssociations.Items.Add(association.Name);
            newItem.ImageKey = (this.imlThumbNail.Images.Count - 1).ToString();
            newItem.Tag = association;
            newItem.ToolTipText = newItem.Text;
        }

        /// <summary>
        /// Scrolls to picture.
        /// </summary>
        private void ScrollToPicture()
        {
            if (this.lvWorkflowAssociations.SelectedItems.Count > 0)
            {
                WorkflowAssociation workflowAssociation = this.lvWorkflowAssociations.SelectedItems[0].Tag as WorkflowAssociation;
                if (workflowAssociation != null && workflowAssociation.Picture != null)
                {
                    this.ControllerInstance.SelectPictureByDocument(this.DocumentKey, workflowAssociation.Picture.Key);
                }
            }
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the lvWorkflowAssociations control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void lvWorkflowAssociations_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.btnScrollToImage.Enabled = this.lvWorkflowAssociations.SelectedItems.Count != 0;
        }

        /// <summary>
        /// Handles the Click event of the btnScrollToImage control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnScrollToImage_Click(object sender, EventArgs e)
        {
            this.ScrollToPicture();
        }

        /// <summary>
        /// Handles the DoubleClick event of the lvWorkflowAssociations control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void lvWorkflowAssociations_DoubleClick(object sender, EventArgs e)
        {
            this.ScrollToPicture();
        }

        #endregion

        #region ITaskPaneControl Members

        /// <summary>
        /// Sets the dock position.
        /// </summary>
        /// <param name="dockPosition">The dock position.</param>
        public void SetDockPosition(TaskPaneDockPositions dockPosition)
        {
            if ((dockPosition & (TaskPaneDockPositions.Left | TaskPaneDockPositions.Right | TaskPaneDockPositions.Floating)) != 0)
            {
                this.SetVertical(true);
            }
            else
            {
                this.SetVertical(false);
            }
        }

        /// <summary>
        /// Sets the vertical.
        /// </summary>
        /// <param name="controlIsVertical">if set to <c>true</c> [control is vertical].</param>
        private void SetVertical(bool controlIsVertical)
        {
            this.SuspendLayout();
            int splitterDistance;
            if (controlIsVertical)
            {
                splitterDistance = this.Height / 2;
            }
            else
            {
                splitterDistance = this.Width / 2;
            }

            // splitcontainter orientation is opposite of the control's orientation
            this.splitContainer1.Orientation = controlIsVertical ? Orientation.Horizontal : Orientation.Vertical;
            this.splitContainer1.SplitterDistance = splitterDistance;

            this.ResumeLayout();
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Releases all resources used by the <see cref="T:System.ComponentModel.Component"/>.
        /// </summary>
        void IDisposable.Dispose()
        {
            this.Disposing(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes the specified the privately held resources.
        /// </summary>
        /// <param name="disposing">if set to <c>true</c> disposes resources.</param>
        private void Disposing(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    this.ControllerInstance.OnAssociationChanged -= new EventHandler<AssociationChangedEventArgs>(Controller_OnAssociationChanged);
                    base.Disposing();
                }
            }

            this.disposed = true;
        }

        #endregion
    }
}
